package venus

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	log "github.com/sirupsen/logrus"
)

type ServerConfig struct {
	Domain        string
	ListenAddr    string
	RewardingHost string
	Pem           string
	Pri           string
	Proxy         string
	Cache         string
	Timeout       int
	NoHTTPS       bool
	HEADERS map[string]string
}

var (
	ConfigDocument = `
## this is demo config file
## impplemented as nginx's upstream proxy

## listen is for listenning  server's addr , if use in outer , should add outer ip
listen: localhost:8443
## default use https
nohttps: false 

## reward , this is which host you want to reward to .
reward: https://www.google.com


## Domain , if your server has domain you should set  this , besides 
# Domain: domain.com

## proxy can set as ss/ssr/vmess/ording https url / tor / socks5
# proxy: ss://xxxxxxx
# proxy: ssr:/xxxxx
# proxy: vmess://xxxxxx
# proxy: https://ssr-or-vmess-or-ss-ording-address.com/uid=xxx
# proxy: tor://
# proxy: socks5://127.0.0.1:1080
proxy: socks5://127.0.0.1:1091
# proxy-timeout set proxy connect's timeout
proxy-timeout: 40
 
## Certifications area ， if not set will use default!
## pri file path
# pri: res/key.pri
## pem file path
# pem: res/cert.pem

## cache if set  will save req in cache path
# cache: /tmp/CacheReq
## Other is hEADERS
`
)

func ReadConf(f string) (config *ServerConfig) {
	fp, err := os.Open(f)
	if err != nil {
		log.Fatal(err)
	}
	defer fp.Close()
	config = new(ServerConfig)
	config.HEADERS = make(map[string]string)
	scanner := bufio.NewScanner(fp)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, ":") {
			fs := strings.SplitN(line, ":", 2)
			head, body := strings.TrimSpace(fs[0]), strings.TrimSpace(fs[1])
			switch head {
			case "domain":
				log.Info(fmt.Sprintf("[%s]: %s", head, body))
				config.Domain = body
			case "reward":

				log.Info(fmt.Sprintf("[%s]: %s", head, body))
				config.RewardingHost = body
			case "listen":

				log.Info(fmt.Sprintf("[%s]: %s", head, body))
				config.ListenAddr = body
			case "pem":

				log.Info(fmt.Sprintf("[%s]: %s", head, body))
				config.Pem = body
			case "proxy":

				log.Info(fmt.Sprintf("[%s]: %s", head, body))
				config.Proxy = body
			case "pri":

				log.Info(fmt.Sprintf("[%s]: %s", head, body))
				config.Pri = body
			case "proxy-timeout":
				log.Info(fmt.Sprintf("[%s]: %s sec", head, body))
				config.Timeout, _ = strconv.Atoi(body)
			case "nohttps":
				log.Info(fmt.Sprintf("[%s]: %s sec", head, body))

				if body == "true" {
					config.NoHTTPS = true
				}
			case "cache":

				log.Info(fmt.Sprintf("[%s]: %s", head, body))
				config.Cache = body
			default:
				if !strings.HasPrefix(head,"#"){
					config.HEADERS[head] = body
					log.Info("Setting HEADER",fmt.Sprintf("HEADER: %s => %s",head,body))	
				}
				
			}
		}
	}

	return
}

func ShowDemo() {
	fmt.Println(ConfigDocument)
}

func (conf *ServerConfig) Marshal() string {
	b, _ := json.MarshalIndent(conf, "", "\t")
	return string(b)
}

func (conf *ServerConfig) StartServer() {

	u, _ := url.Parse(conf.RewardingHost)
	Domain = conf.Domain
	HostAddr = conf.ListenAddr
	DEFAULT_SCHEME = u.Scheme
	var lst net.Listener
	var err error
	// var backlst net.Listener
	if conf.NoHTTPS {
		HostAddrScheme = "http"
		lst, err = net.Listen("tcp", conf.ListenAddr)
		defer lst.Close()
		HostAddr = "http"
	} else {
		tls := ReadFromFileToTlsConfig(conf.Pem, conf.Pri)
		tls.Server = conf.ListenAddr
		lst, err = tls.WithTlsListener()
		defer lst.Close()
	}
	if err != nil {
		log.Error(err)
	}
	relayer := NewRelayer(u.Host, conf.Proxy)
	DeFaultTimeout = time.Duration(conf.Timeout) * time.Second
	if conf.Cache != "" {
		UseFileSave = true
		DefaultDir = conf.Cache
		os.Mkdir(DefaultDir, os.ModePerm)
	}

	go relayer.Datas.RunInBack()
	if err != nil {
		log.Fatal(err)
	}
	connectionNum := 0
	for {
		connectionNum++
		// fmt.Printf("[%d]  \t", connectionNum)
		l, err := lst.Accept()

		// fmt.Printf("ok %s \t\n", connectionNum, l.RemoteAddr())
		if err != nil {
			log.Fatalf("Break ! : %v", err)
			break
		}
		go func(c net.Conn) {
			defer func() {
				c.Close()
				connectionNum--
			}()

			if err := relayer.Handle(c, conf); err != nil {
				log.Error(err)
			}
		}(l)
	}
}
